package com.stars.easyms.dubbo.filter;

import com.stars.easyms.base.constant.HttpHeaderConstants;
import com.stars.easyms.base.trace.EasyMsTraceSynchronizationManager;
import com.stars.easyms.base.util.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.*;

import java.util.*;

import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER;

/**
 * <p>className: EasyMsDubboConsumerFilter</p>
 * <p>description: dubbo的消费者端过滤器</p>
 *
 * @author guoguifang
 * @version 1.7.0
 * @date 2020/11/13 3:02 下午
 */
@Slf4j
@Activate(group = CONSUMER)
public class EasyMsDubboConsumerFilter extends BaseEasyMsDubboFilter {

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) {
        // 获取接口类型
        String serviceName = invocation.getServiceName();
        if (isDubboInnerService(serviceName)) {
            return invoker.invoke(invocation);
        }

        // 获取traceId，如果不存在则创建新的traceId
        String traceId = TraceUtil.getTraceId(EasyMsTraceSynchronizationManager.getTraceId());
        String upstreamRequestId = EasyMsTraceSynchronizationManager.getRequestId();
        String downstreamRequestId = TraceUtil.getTraceId();
        String asyncId = EasyMsTraceSynchronizationManager.getAsyncId();

        // 获取请求信息
        String requestPath = serviceName + "." + invocation.getMethodName();
        String requestSys = SpringBootUtil.getApplicationName();
        String requestTime = DateTimeUtil.now();

        // 将请求信息存入dubbo的附件中
        EasyMsDubboRequestInfo easyMsDubboRequestInfo = new EasyMsDubboRequestInfo();
        easyMsDubboRequestInfo.setRequestPath(requestPath);
        easyMsDubboRequestInfo.setRequestSys(requestSys);
        easyMsDubboRequestInfo.setRequestTime(requestTime);
        easyMsDubboRequestInfo.setRequestId(downstreamRequestId);
        if (asyncId != null) {
            easyMsDubboRequestInfo.setAsyncId(asyncId);
        }

        // 这里必须使用RpcContext.getContext().getObjectAttachments()，而不能使用invocation.getObjectAttachments()，
        // 因为在传输时RpcContext.getContext().getObjectAttachments()会覆盖掉invocation.getObjectAttachments()的值
        Map<String, Object> objectAttachments = RpcContext.getContext().getObjectAttachments();
        objectAttachments.put(HttpHeaderConstants.TRACE_KEY, traceId);
        objectAttachments.put(HttpHeaderConstants.DUBBO_REQUEST_INFO, easyMsDubboRequestInfo);

        // invocation的Attributes属性不会传输只在本应用有效，由于消费可以使用异步，当异步的时候ThreadLocal会失效，
        // 但使用invocation的Attributes可在异步时正常获取到
        invocation.put(HttpHeaderConstants.TRACE_KEY, traceId);
        invocation.put(HttpHeaderConstants.HEADER_KEY_REQUEST_ID, upstreamRequestId);
        invocation.put(HttpHeaderConstants.DUBBO_REQUEST_INFO, easyMsDubboRequestInfo);

        // 记录调用信息
        log.info("[调用服务-请求]-[接口类型: {}]-[请求系统: {}]-[链路Id: {}]-[请求ID: {}]{}-[请求时间: {}]{}.",
                requestPath, requestSys, traceId, downstreamRequestId, TraceUtil.getAsyncIdTrace(asyncId), requestTime,
                easyMsDubboProperties.isLogRequest() ? "-请求数据: " + JsonUtil.toJSONString(invocation.getArguments()) : "");
        return invoker.invoke(invocation);
    }

    @Override
    public void onResponse(Result appResponse, Invoker<?> invoker, Invocation invocation) {
        // 获取接口类型
        String serviceName = invocation.getServiceName();
        if (isDubboInnerService(serviceName)) {
            return;
        }
        // 记录日志信息
        logInfo(appResponse.getException(), invocation, appResponse.getValue());
    }

    @Override
    public void onError(Throwable t, Invoker<?> invoker, Invocation invocation) {
        if (!isDubboInnerService(invocation.getServiceName())) {
            logInfo(t, invocation, null);
        }
    }

    private void logInfo(Throwable t, Invocation invocation, Object value) {
        String traceId = (String) invocation.get(HttpHeaderConstants.TRACE_KEY);
        EasyMsDubboRequestInfo easyMsDubboRequestInfo =
                (EasyMsDubboRequestInfo) invocation.get(HttpHeaderConstants.DUBBO_REQUEST_INFO);
        if (traceId == null || easyMsDubboRequestInfo == null) {
            return;
        }
        String requestSys = easyMsDubboRequestInfo.getRequestSys();
        String requestPath = easyMsDubboRequestInfo.getRequestPath();
        String downstreamRequestId = easyMsDubboRequestInfo.getRequestId();
        String asyncId = easyMsDubboRequestInfo.getAsyncId();

        // 如果使用的是异步响应则需要重新设置trace信息
        String upstreamRequestId = (String) invocation.get(HttpHeaderConstants.HEADER_KEY_REQUEST_ID);
        EasyMsTraceSynchronizationManager.setTraceInfo(traceId, upstreamRequestId);
        if (asyncId != null) {
            EasyMsTraceSynchronizationManager.setAsyncId(asyncId);
        }

        // 记录调用响应日志
        if (t != null) {
            log.error("[调用服务-响应-异常]-[接口类型: {}]-[请求系统: {}]-[链路Id: {}]-[请求ID: {}]{}-[接收响应时间: {}]-异常信息: ",
                    requestPath, requestSys, traceId, downstreamRequestId, TraceUtil.getAsyncIdTrace(asyncId), DateTimeUtil.now(), t);
        } else {
            log.info("[调用服务-响应]-[接口类型: {}]-[请求系统: {}]-[链路Id: {}]-[请求ID: {}]{}-[接收响应时间: {}]{}.",
                    requestPath, requestSys, traceId, downstreamRequestId, TraceUtil.getAsyncIdTrace(asyncId), DateTimeUtil.now(),
                    easyMsDubboProperties.isLogResponse() ? "-响应数据: " + JsonUtil.toJSONString(value) : "");
        }
    }
}
